package graph_viewer.structure;

import java.util.*;
import java.awt.*;
import javax.swing.*;

import graph_viewer.animator.*;
import graph_viewer.canvas.*;

import graph_viewer.factory.*;
import graph_viewer.structure.clustering.*;
import graph_viewer.util.*;

/** 
	the leaf nodes of the cluster_tree are the vertices of the clustered graph.
	<p>

	@author Wanchun Li
	@version %I% %G%
	@since 19/04/2004
*/

public class Clustered_Graph extends Basic_Graph{
	private Tree		cluster_tree;
	private ArrayList	clustered_graph_edges; // node label pairs of string[]

	//============================================================================
	// construtors
	//============================================================================
	public Clustered_Graph( Tree cluster_tree ){
		this.cluster_tree	= cluster_tree;		
	}
	
	public Clustered_Graph( Tree cluster_tree, ArrayList clustered_graph_edges){
		this( cluster_tree );
		this.clustered_graph_edges	= clustered_graph_edges;
	}
	
	public Clustered_Graph( Clustered_Graph a_clustered_graph ){
		
		Node root	 = Structure_Util.find_graph_root( a_clustered_graph );

		Tree	 a_tree	= new Generic_Tree( a_clustered_graph.get_cluster_tree() );		
		this.cluster_tree	= new Generic_Tree( a_tree );
		this.clustered_graph_edges	= new ArrayList( a_clustered_graph.get_edges() );	

		Node new_root	= search( root.get_label() );	
		cluster_tree.set_root( new_root );
	}

	//============================================================================
	// implemented public methods
	//============================================================================	
	public void	traverse( Command action ){
		Collection coll = find_graph_nodes();

		for ( Iterator _i = coll.iterator(); _i.hasNext(); ){
			Node a_node = (Node)_i.next();
			action.do_action( a_node );
		}
	}
	
	public Node search( String label ){
		// use the Graph.search()
		return cluster_tree.search( find_graph_nodes(), label );
	}

	public void allocate_DOI( Node focus_node ){
		cluster_tree.allocate_DOI( focus_node );
	}

	/*public void reallocate_DOI( int threshold_value ){
		cluster_tree.reallocate_DOI( threshold_value );
	}*/

	public Tree	find_tree(){
		return get_cluster_tree();
	}

	public ArrayList	find_edges( ){
		return get_edges();
	}

	public ArrayList	find_nodes(){//adapter
		return get_cluster_tree().find_nodes();
	}

	public void	add_node( Node a_node ){
		get_cluster_tree().add_node( a_node );
	}

	//@param node_replacing_edge if the related edges need implicitely replaced by a nodes
	public void delete_node( Node a_node ) {
		get_cluster_tree().delete_node( a_node );
	}

	//update an edge replacing @a_node by @replaced_node when @a_node is deleted.
	public void update_related_cluster_edges( Node a_node, Node replaced_node ){
		if ( ! a_node.is_leaf() ){
			return ;
		}

		if ( a_node.is_root() ){
			clustered_graph_edges = new ArrayList();
			return;
		}

		String
			node_label				= a_node.get_label(),
			replaced_node_label	= replaced_node.get_label();
		
		//ArrayList updated_clustered_edge	= new ArrayList();
		HashSet	updated_clustered_edge = new HashSet();

		for ( int i = 0; i < clustered_graph_edges.size(); i++  ){
			String[] edge_label_pair = (String[])clustered_graph_edges.get( i );
						
			if ( edge_label_pair[0].equals(node_label) ){
				Debug.println( "replace edge label: " + edge_label_pair[0] + " by " + replaced_node_label , 3 );
				edge_label_pair[0] = replaced_node_label;				
			} else if ( edge_label_pair[1].equals(node_label) ){
				Debug.println( "replace edge label: "+ edge_label_pair[0] + " by " + replaced_node_label , 3 );
				edge_label_pair[1] = replaced_node_label;
			}
			
			if (! edge_label_pair[0].equals( edge_label_pair[1] ) ){
				updated_clustered_edge.add( edge_label_pair );
			}
			
		}//end of for

		clustered_graph_edges = new ArrayList( updated_clustered_edge );
	}

	public void delete_related_cluster_edges( Node a_node ){
		if (! a_node.is_leaf() ){
			return ;
		}
		String node_label	= a_node.get_label();

		ArrayList removed_edges	= new ArrayList();
		
		for ( int i = 0; i < clustered_graph_edges.size(); i++  ){
			String[] edge_label_pair = (String[])clustered_graph_edges.get( i );
			
			if ( edge_label_pair[0].equals(node_label) || edge_label_pair[1].equals(node_label) ){
				//clustered_graph_edges.remove( i );
				removed_edges.add( edge_label_pair );
			}
		}	

		for ( Iterator _i = removed_edges.iterator(); _i.hasNext(); ){
			String[] removed_edge_label = (String[])_i.next();
			
			clustered_graph_edges.remove( removed_edge_label );
		}
	}

	

	//============================================================================
	// implemented methods: template hookers
	//============================================================================
	/**
		All nodes, including leaves, are considered as clusters. 
		After distortion:
			If a cluster is closed, do nothing.
			If a cluster is opened:
				case: all children are filtered, close the cluster
				case: all children are not filtered, do nothing
				case: some children are filtered, create a new psuedo cluster to contain the filtered children.
	*/
	public void do_cluster( int thresholding, Graph_Canvas graph_canvas ){
		int i	= cluster_tree.height();

		for ( ; i >=0 ; i--){
			Debug.println( "Do cluster in level: " + i + ", focus is: " +  graph_canvas.get_canvas_metrics().get_focus(), 25 );
			// find all nodes of height i; 
			do_cluster_in_one_level( i, thresholding, find_edges(), graph_canvas );
		}
	}
	// end of template hookers

	
	public void do_pre_clustering_animation( int thresholding, Graph_Canvas graph_canvas ){
		int i	= cluster_tree.height();
		//Debug.println( "GRAPH.do_pre_cluster_animation on graph_canvas...", 3 );
		for ( ; i >=1 ; i--){
			// find all nodes of height i; 
			do_pre_cluster_animation_in_one_level( i, thresholding, find_edges(), graph_canvas );
		}
	}

	
	public void do_post_clustering_animation( int thresholding, Graph_Canvas graph_canvas ){
		int i	= cluster_tree.height();
		//Debug.println( "GRAPH.do_post_cluster_animation on graph_canvas...", 3 );
		for ( ; i >= 1; i--){
			// find all nodes of height i; 
			do_post_cluster_animation_in_one_level( i, thresholding, find_edges(), graph_canvas );
		}
	}
	//============================================================================
	// private methods
	//============================================================================
	/**
		@param	height	the height of a node in cluster tree.
	*/
	private void do_cluster_in_one_level( int height, int thresholding, ArrayList edges, Graph_Canvas graph_canvas ){
		ArrayList all_same_height_node = find_displayed_same_height_nodes( height );

		Debug.println( "\tIn level: " + height + ", " + all_same_height_node.size() + " nodes displayed.", 26 );
		for (Iterator _i = all_same_height_node.iterator(); _i.hasNext(); ){
			String		node_label	= (String)_i.next();
			Node		node			= search( node_label );

			Debug.println( "\t Test clustering strategy for " + node, 26 );			
			
			if ( node.get_is_closed() ){
				Debug.println( "\t " + node + " is closed!", 26 );			
	
				continue;
			}

			Graph_Cluster_Strategy gcs	= new Graph_Cluster_Strategy_Factory().create( node );
			Debug.println( "\t" + gcs + " do_cluster_action:"  + node , 26 );

			gcs.do_cluster_action( this, node, graph_canvas );

		}
	}
	
	private void do_pre_cluster_animation_in_one_level( int height, int thresholding, ArrayList edges, Graph_Canvas graph_canvas ){
		ArrayList all_same_height_node = find_displayed_same_height_nodes( height );
		
		ArrayList	 animator_metrics_in_the_level	= new ArrayList();
		for (Iterator _i = all_same_height_node.iterator(); _i.hasNext(); ){
			Debug.println( "\tIn level: " + height + ",  do_pre_cluster_animation_in_one_level.", 26 );
			String		node_label	= (String)_i.next();
			Node		node			= search( node_label );
			
			if ( Structure_Util.is_parent_of_leaf_clusters( node ) ){
				Graph_Cluster_Strategy gcs	= new Graph_Cluster_Strategy_Factory().create( node );
				
				ArrayList	a_ms	= gcs.build_pre_clustering_animation_metrics( graph_canvas, node.get_label() );
				if ( a_ms != null ){
					animator_metrics_in_the_level.addAll( a_ms );
				}			
			}			
		}
		
		new Animator_Actor().do_multiple_animation( animator_metrics_in_the_level, graph_canvas );
	}

	private void do_post_cluster_animation_in_one_level( int height, int thresholding, ArrayList edges, Graph_Canvas graph_canvas ){
		ArrayList all_same_height_node = find_displayed_same_height_nodes( height );
		
		ArrayList	 animator_metrics_in_the_level	= new ArrayList();
		for (Iterator _i = all_same_height_node.iterator(); _i.hasNext(); ){
			String		node_label	= (String)_i.next();
			Node		node			= search( node_label );
			
			if ( Clustering_Util.has_pseudo_child( node ) ){
				Debug.println( "\t:do_post_cluster_animation_in_one_level: " + node + " has_pseudo_child!", 25 );

				ArrayList	a_ms	= //new Create_Pseudo_Cluster_Strategy()
									new Graph_Cluster_Strategy_Factory().create( node ).build_post_clustering_animation_metrics( graph_canvas, node.get_label() );
				if ( a_ms != null ){
					animator_metrics_in_the_level.addAll( a_ms );
				}			
			}			
		}
		
		new Animator_Actor().do_multiple_animation( animator_metrics_in_the_level, graph_canvas );
		/**/
	}

	boolean is_filtered_node( Node node, Node focus_node ){
		boolean is_filtered	= false;

		String 
			node_label = node.get_label(),
			focus_label	= focus_node.get_label(),
			node_parent_label	= node.get_parent().get_label(),
			focus_parent_label	= focus_node.get_parent().get_label();

		if	(	node.is_leaf_cluster() 
					&&	( ! node_label.equals(focus_label ) )
					&&	 ( ! node_parent_label.equals( focus_parent_label ) )
					&&	 (	! Structure_Util.has_edge(this, node, focus_node ) )
			)
		{
			is_filtered	= true;	
		}

		return is_filtered;
	}

	ArrayList clone_edges(){
		ArrayList edges	= new ArrayList();

		for ( Iterator _i = clustered_graph_edges.iterator(); _i.hasNext(); ){
			String[] an_edge	= (String[])_i.next();

			String[] an_clone_edge = new String[] { an_edge[0], an_edge[1] };

			edges.add( an_clone_edge );
		}

		return edges;
	}
	
	private ArrayList find_graph_nodes(){
		return cluster_tree.get_tree();
	}

	//============================================================================
	// getter & setter
	//============================================================================
	public void set_cluster_tree( Tree a_tree ){
		this.cluster_tree	= a_tree;
	}
	
	public Tree get_cluster_tree(){
		return this.cluster_tree;
	}

	
	public void set_edges( ArrayList clustered_graph_edges ){
		this.clustered_graph_edges	= clustered_graph_edges;
	}


	public ArrayList get_edges(){
		return this.clustered_graph_edges;
	}

	public String	get_class_name(){
		return Visual_Component.CLUSTERED_GRAPH;
	}

}